package org.yankun.exam4j.session;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;



@Component
public class SessionManager implements InitializingBean{
	
	
	@Autowired(required=false)
	private transient ServletContext sc;
	
	public static final String ATTR_SESSION_DATA = "EXAM4J_USER_SESSION_DATA";

	/**
	 * key 是 SessionId，value 是 SessionData
	 */
	private transient ConcurrentMap<String,UserSessionData> sessions = new ConcurrentHashMap<String, UserSessionData>();
	
	
	/**
	 * 当会话从持久存储中恢复时，如果已登录，则将其保存并跟踪
	 * @param session
	 */
	public void onSessionDidActivate(HttpSession session) {
		UserSessionData data = getSessionData(session);
		if (data!= null) {
			this.sessions.put(data.getSessionId(), data);
		}
	}
	
	
	/**
	 * 当角色权限变更时，重新加载所有已登录用户的权限数据
	 */
	public void onAuthorityChanged() {
		// TODO 加载所有权限数据
		
		for (@SuppressWarnings("unused") UserSessionData data: sessions.values()) {
			// TODO 重新加载 权限
		}
	}
	
	/**
	 * 当用户权限变更时，重新加载它
	 */
	public void onAuthorityChanged(Serializable userId) {
		for (@SuppressWarnings("unused") UserSessionData data: sessions.values()) {
			// TODO 判断已登录用户中是否有与参数 userId 对应的用户，如果有，重新加载它
			//	注意，可能有多个相同的用户登录了!
		}
	}
	
	/**
	 * 当前会话设置用户会话数据，表示已登录
	 * @param session
	 * @param data
	 * @return
	 */
	public UserSessionData login(HttpSession session, UserSessionData data) {
		assert(session!=null);
		assert(data!=null);
		
		UserSessionData old = setSessionData(session, data);
		
		assert (old != data);
		
		/**
		 * 如果原来会话中有 SessionData ， 将其从 sessions Map 中移除
		 */
		if (old != null) {
			this.sessions.remove(old.getSessionId(), old);
		}
		
		this.sessions.put(data.getSessionId(), data);
		return old;
	}
	
	
	public UserSessionData logout(HttpSession session) {
		UserSessionData old =  setSessionData(session, null);
		
		if (old != null) {
			this.sessions.remove(old.getSessionId(), old);
		}
			
		return old;
	}
	
	
	/**
	 * 从 Http Session 中读取用户主键
	 * @param session
	 * @return
	 */
	public static Serializable getUserId(HttpSession session) {
		if(null==session){
			return null;
		}
		UserSessionData data = getSessionData(session);
		return data!=null ? data.getUserId() : null;
	}
	
	/**
	 * 从 Http Session 中读取会话数据
	 * @param session
	 * @return
	 */
	public static UserSessionData getSessionData(HttpSession session) {
		return (UserSessionData) session.getAttribute(ATTR_SESSION_DATA); 
	}
	
	static UserSessionData setSessionData(HttpSession session, UserSessionData data) {
		synchronized (session) {
			UserSessionData old = getSessionData(session);
			session.setAttribute(ATTR_SESSION_DATA, data);
			return old;
		}
	}
	
	/**
	 * 判断 Http Session 是否已登录
	 * @param session
	 * @return
	 */
	public static boolean isLogon(HttpSession session) {
		return getUserId(session) != null;
	}



	@SuppressWarnings("unchecked")
	@Override
	public void afterPropertiesSet() throws Exception {
		if (sc != null) {
			ArrayList<UserSessionData> al = (ArrayList<UserSessionData>)
					sc.getAttribute(UserSessionData.ATTR_USER_SESSION_DATA_RESTORE_QUEUE);
			if (al!= null) {
				for (UserSessionData data : al) {
					this.sessions.put(data.getSessionId(), data);
				}
			}
		}
	}


}
